package com.gzhryc.pay.adaptation.weixin;

import com.gzhryc.common.StringTools;
import com.gzhryc.common.json.JsonTools;
import com.gzhryc.common.logging.Logger;
import com.gzhryc.common.secruity.AESTools;
import com.gzhryc.common.secruity.EncryptTools;
import com.gzhryc.pay.adaptation.weixin.msg.WXPayNotify;
import com.gzhryc.pay.adaptation.weixin.msg.advance_pay.WXPayAdvancePayConfirmNotify;
import com.gzhryc.pay.adaptation.weixin.msg.advance_pay.WXPayAdvancePayFinishNotify;
import com.gzhryc.pay.adaptation.weixin.msg.advance_pay.WXPayAdvancePayNotify;
import com.gzhryc.pay.adaptation.weixin.msg.native_pay.WXPayNativePayNotify;
import com.gzhryc.pay.adaptation.weixin.msg.normal.WXPayNormalPayNotify;
import com.gzhryc.pay.adaptation.weixin.msg.normal.WXPayNormalRefundNotify;
import org.apache.commons.lang3.StringUtils;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.*;

/**
 * 支付工具类
 * 
 * @author 张浩
 * @version 0.3.10
 */
public class WXPayUtils implements Serializable {

	private static final long serialVersionUID = 1L;
	
	static Logger log = Logger.getLogger(WXPayUtils.class);

	/**
	 * 生成随即字符串
	 * 
	 * @return
	 */
	public static String buildNonceStr() {
		UUID uuid = UUID.randomUUID();
		return uuid.toString().replace("-", "");
	}

	/**
	 * 生成时间戳
	 * 
	 * @return
	 */
	public static Long buildTimestamp() {
		return System.currentTimeMillis() / 1000;
	}

	public static <T> String buildSign(T request, Class<T> clazz, String key) {
		List<String> list = new ArrayList<>();
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {
			if (field.getName().equals("sign") || Modifier.isStatic(field.getModifiers())) {
				continue;
			}
//			if (field.getName().equals("coupon_fee") || field.getName().equals("coupon_count")){
//				continue;
//			}
			field.setAccessible(true);
			try {
				Object obj = field.get(request);
				if (obj != null && StringUtils.isNotBlank(obj.toString())) {
					list.add(field.getName() + "=" + obj);
				}
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}

		Collections.sort(list);
		list.add("key=" + key);

		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < list.size(); i++) {
			String a = list.get(i);
			sb.append(a);
			if (i != list.size() - 1) {
				sb.append('&');
			}
		}
		return EncryptTools.md5(sb.toString()).toUpperCase();
	}

	public static String buildSHA256Sign(Map<String, Object> data, String key) {
		List<String> list = new ArrayList<>();
		for (String name : data.keySet()) {
			list.add(name + "=" + data.get(name));
		}
		Collections.sort(list);
		list.add("key=" + key);

		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < list.size(); i++) {
			String a = list.get(i);
			sb.append(a);
			if (i != list.size() - 1) {
				sb.append('&');
			}
		}
		try {
			Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
			SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
			sha256_HMAC.init(secret_key);
			byte[] array = sha256_HMAC.doFinal(sb.toString().getBytes("UTF-8"));
			StringBuilder temp = new StringBuilder();
			for (byte item : array) {
				temp.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
			}
			return temp.toString().toUpperCase();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * V3版本-Navtive支付回调解码
	 * @param notify
	 * @param apiV3Key
	 * @return
	 */
	public static WXPayNativePayNotify getWXPayNativePayNotify(WXPayNotify notify, String apiV3Key){
		if(notify != null && StringUtils.isNotBlank(notify.getEvent_type())
				&& notify.getResource() != null && StringUtils.isNotBlank(apiV3Key)) {
			//解码
			try {
				byte[] jsonByte = AESTools.get().decrypt(Base64.getDecoder().decode(notify.getResource().getCiphertext()),apiV3Key.getBytes()
						,notify.getResource().getAssociated_data().getBytes(), notify.getResource().getNonce().getBytes());
				if(jsonByte != null && jsonByte.length > 0) {
					String json = new String(jsonByte,"UTF-8");
					return JsonTools.fromJson(json, WXPayNativePayNotify.class);
				}
			} catch (Exception e) {
				log.error(e.getMessage(),e);
			}
		}
		return null;
	}

	public static WXPayNormalPayNotify getWXPayNormalPayNotify(WXPayNotify notify, DefaultWXPayConfig config){
		if(notify != null && config != null && StringUtils.isNotBlank(notify.getEvent_type())
				&& notify.getResource() != null && StringUtils.isNotBlank(config.getApiV3Key())) {
			//解码
			try {
				byte[] jsonByte = AESTools.get().decrypt(Base64.getDecoder().decode(notify.getResource().getCiphertext()), config.getApiV3Key().getBytes()
						,notify.getResource().getAssociated_data().getBytes(), notify.getResource().getNonce().getBytes());
				if(jsonByte != null && jsonByte.length > 0) {
					String json = new String(jsonByte,"UTF-8");
					return JsonTools.fromJson(json, WXPayNormalPayNotify.class);
				}
			} catch (Exception e) {
				log.error(e.getMessage(),e);
			}
		}else{
			log.error("参数错误，微信支付回调内容：{{0:json}}",notify);
			log.error("参数错误，微信配置内容：{{0:json}}",config);
		}
		return null;
	}

	public static WXPayAdvancePayConfirmNotify getWXPayAdvancePayConfirmNotify(WXPayAdvancePayNotify notify, DefaultWXPayConfig config){
		if(notify != null && config != null && StringUtils.isNotBlank(notify.getEvent_type())
				&& notify.getResource() != null && StringUtils.isNotBlank(config.getApiV3Key())) {
			//解码
			try {
				byte[] jsonByte = AESTools.get().decrypt(Base64.getDecoder().decode(notify.getResource().getCiphertext()), config.getApiV3Key().getBytes()
						,notify.getResource().getAssociated_data().getBytes(), notify.getResource().getNonce().getBytes());
				if(jsonByte != null && jsonByte.length > 0) {
					String json = new String(jsonByte,"UTF-8");
					return JsonTools.fromJson(json, WXPayAdvancePayConfirmNotify.class);
				}
			} catch (Exception e) {
				log.error(e.getMessage(),e);
			}
		}else{
			log.error("参数错误，微信支付回调内容：{{0:json}}",notify);
			log.error("参数错误，微信配置内容：{{0:json}}",config);
		}
		return null;
	}

	public static WXPayAdvancePayFinishNotify getWXPayAdvancePayFinishNotify(WXPayAdvancePayNotify notify, DefaultWXPayConfig config){
		if(notify != null && config != null && StringUtils.isNotBlank(notify.getEvent_type())
				&& notify.getResource() != null && StringUtils.isNotBlank(config.getApiV3Key())) {
			//解码
			try {
				byte[] jsonByte = AESTools.get().decrypt(Base64.getDecoder().decode(notify.getResource().getCiphertext()), config.getApiV3Key().getBytes()
						,notify.getResource().getAssociated_data().getBytes(), notify.getResource().getNonce().getBytes());
				if(jsonByte != null && jsonByte.length > 0) {
					String json = new String(jsonByte,"UTF-8");
					return JsonTools.fromJson(json, WXPayAdvancePayFinishNotify.class);
				}
			} catch (Exception e) {
				log.error(e.getMessage(),e);
			}
		}else{
			log.error("参数错误，微信支付回调内容：{{0:json}}",notify);
			log.error("参数错误，微信配置内容：{{0:json}}",config);
		}
		return null;
	}

	public static WXPayNormalRefundNotify getWXPayNormalRefundNotify(WXPayNotify notify, DefaultWXPayConfig config){
		if(notify != null && config != null && StringUtils.isNotBlank(notify.getEvent_type())
				&& notify.getResource() != null && StringUtils.isNotBlank(config.getApiV3Key())) {
			//解码
			try {
				byte[] jsonByte = AESTools.get().decrypt(Base64.getDecoder().decode(notify.getResource().getCiphertext()), config.getApiV3Key().getBytes()
						,notify.getResource().getAssociated_data().getBytes(), notify.getResource().getNonce().getBytes());
				if(jsonByte != null && jsonByte.length > 0) {
					String json = new String(jsonByte,"UTF-8");
					return JsonTools.fromJson(json, WXPayNormalRefundNotify.class);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return null;
	}

	public static String buildRentBillH5String(String packageData, DefaultWXPayConfig config) {
		Map<String, Object> data = new HashMap<String, Object>();
		data.put("mch_id", config.getMchId());
		data.put("package", packageData);
		Long timestamp = WXPayUtils.buildTimestamp();
		data.put("timestamp", timestamp);
		String nonceStr = WXPayUtils.buildNonceStr();
		data.put("nonce_str", nonceStr);
		data.put("sign_type", "HMAC-SHA256");
		String sign = buildSHA256Sign(data, config.getPayKey());
		return "mch_id=" + config.getMchId() + "&package=" + packageData + "&timestamp=" + timestamp + "&nonce_str=" + nonceStr + "&sign_type=HMAC-SHA256&sign="
				+ sign;
	}

	public static String buildRentBillXCXJsonStr(String packageData, DefaultWXPayConfig config) {
		Map<String, Object> data = new HashMap<String, Object>();
		data.put("mch_id", config.getMchId());
		data.put("package", packageData);
		data.put("timestamp", WXPayUtils.buildTimestamp());
		data.put("nonce_str", WXPayUtils.buildNonceStr());
		data.put("sign_type", "HMAC-SHA256");
		String sign = buildSHA256Sign(data, config.getPayKey());
		data.put("sign", sign);
		return JsonTools.toJson(data);
	}

	public static PrivateKey readPrivateKey(String privateKeyContent) {
		if (StringTools.isNotBlank(privateKeyContent)) {
			try {
				String privateKeyStr = privateKeyContent.replace("-----BEGIN PRIVATE KEY-----", "")
						.replace("-----END PRIVATE KEY-----", "").replaceAll("\\s+", "");
				//log.info("微信PrivateKey = {{0}}",privateKeyStr);
				KeyFactory kf = KeyFactory.getInstance("RSA");
				return kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyStr)));
			} catch (NoSuchAlgorithmException e) {
				log.error(e.getMessage(),e);
				throw new RuntimeException("当前Java环境不支持RSA", e);
			} catch (InvalidKeySpecException e) {
				log.error(e.getMessage(),e);
				throw new RuntimeException("无效的密钥格式");
			}
		}
		return null;
	}
}
